package com.itdct.cozecd

import com.alibaba.fastjson2.JSON
import com.itdct.cozecd.bo.ChatBo
import com.itdct.cozecd.util.logError
import com.itdct.cozecd.util.logInfo
import com.itdct.cozecd.vo.*
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.logging.HttpLoggingInterceptor
import java.io.IOException
import java.util.*
import java.util.concurrent.TimeUnit

/**
 * @author Zhouwx
 * @date 2025/7/18 10:15:12
 * @version 1.0
 * @description
 */
class CozeChat(
    val secretKey: String
) {
    private var okHttpClient: OkHttpClient
    private val logger: HttpLoggingInterceptor
    private var timeout: Long = 10
    private val okHttpClientBuilder: OkHttpClient.Builder
    private var retrieveRetryCount = 0

    init {
        logger = HttpLoggingInterceptor()
        logger.setLevel(HttpLoggingInterceptor.Level.BODY)
        okHttpClientBuilder = OkHttpClient.Builder()
            .addInterceptor(logger)
            .connectTimeout(timeout, TimeUnit.SECONDS)
            .readTimeout(timeout, TimeUnit.SECONDS)
            .writeTimeout(timeout, TimeUnit.SECONDS)
        okHttpClient = okHttpClientBuilder.build()
    }

    fun chat(chatBo: ChatBo, onSuccess: (resultVo: ChatResultVo) -> Unit, onFail: (e: Exception) -> Unit) {
        retrieveRetryCount = 0
        CozeThreadPool.threadPool.submit {
            try {
                doChat(chatBo, onFail, onSuccess)
            } catch (e: Exception) {
                logError(e)
            }
        }
    }

    private fun doChat(
        chatBo: ChatBo,
        onFail: (e: Exception) -> Unit,
        onSuccess: (resultVo: ChatResultVo) -> Unit
    ) {
        val requestJson = JSON.toJSONString(chatBo)
        logInfo("requestJson is $requestJson")
        val toRequestBody = requestJson.toRequestBody("application/json".toMediaType())
        var url = "https://api.coze.cn/v3/chat"
        chatBo.conversationId?.also {
            url = "https://api.coze.cn/v3/chat?conversation_id=$it"
        }
        val request = Request.Builder().url(url)
            .header("Authorization", "Bearer $secretKey")
            .header("Content-Type", "application/json")
            .post(toRequestBody)
            .build()
        val call = okHttpClient.newCall(request)
        call.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                onFail(e)
            }

            override fun onResponse(call: Call, response: Response) {
                try {
                    retrieve(response, onSuccess, onFail)
                } catch (e: Exception) {
                    onFail(e)
                }
            }
        })
    }

    private fun retrieve(response: Response, onSuccess: (resultVo: ChatResultVo) -> Unit, onFail: (e: IOException) -> Unit) {
        response.body?.string()?.let {
            logInfo(it)
            val chatVo = JSON.parseObject(it, ChatVo::class.java)
            if (chatVo.code != 0L) {
                error("code not null, message is ${chatVo.msg}")
            }
            chatVo
        }?.let {
            it.data?.let {
                val chatId = it.id ?: throw RuntimeException("chat_id is null")
                val conversationId = it.conversationId ?: throw RuntimeException("conversation_id is null")
                retrieveData(conversationId, chatId, onSuccess, onFail)
            }

        } ?: run {
            error("chat_data is null")
        }
    }

    private fun retrieveData(
        conversationId: String,
        chatId: String,
        onSuccess: (resultVo: ChatResultVo) -> Unit,
        onFail: (e: IOException) -> Unit
    ) {
        val retrieveRequest = Request.Builder().url("https://api.coze.cn/v3/chat/retrieve?conversation_id=${conversationId}&chat_id=${chatId}")
            .header("Authorization", "Bearer $secretKey")
            .header("Content-Type", "application/json")
            .get()
            .build()
        val retrieveCall = okHttpClient.newCall(retrieveRequest)
        retrieveCall.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                onFail(e)
            }

            override fun onResponse(call: Call, response: Response) {
                response.body?.string()?.let {
                    val retrieveVo = JSON.parseObject(it, RetrieveVo::class.java)
                    retrieveVo.code?.let {
                        if (it != 0L) {
                            error("code not null, message is ${retrieveVo.msg}")
                        }
                    }
                    retrieveVo.data?.let {
                        it.status?.let {
                            if (it == "completed") {
                                // INFO: Zhouwx: 2025/7/18 如果是已完成的，则进入下一步，否则继续请求，直到已完成为止
                                listMessage(conversationId, chatId, onSuccess, onFail)
                            } else {
                                Thread.sleep(2000)
                                retrieveRetryCount++
                                if (retrieveRetryCount > 10) {
                                    error("retrieve_retry_count > 10")
                                } else {
                                    retrieveData(conversationId, chatId, onSuccess, onFail)
                                }
                            }
                        }
                    }
                } ?: run {
                    error("retrieve data is null")
                }
            }
        })
    }

    private fun listMessage(conversationId: String, chatId: String, onSuccess: (messageList: ChatResultVo) -> Unit, onFail: (e: IOException) -> Unit) {
        val listMessageRequest = Request.Builder().url("https://api.coze.cn/v3/chat/message/list?conversation_id=${conversationId}&chat_id=${chatId}")
            .header("Authorization", "Bearer $secretKey")
            .header("Content-Type", "application/json")
            .get()
            .build()
        val listMessageCall = okHttpClient.newCall(listMessageRequest)
        listMessageCall.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                onFail(e)
            }

            override fun onResponse(call: Call, response: Response) {
                response.body?.string()?.let {
                    val listMessageVo = JSON.parseObject(it, MessageListVo::class.java)
                    listMessageVo.data?.let {
                        val chatResultVo = ChatResultVo()
                        chatResultVo.conversationId = conversationId
                        chatResultVo.messageList = it
                        chatResultVo.detail = listMessageVo
                        onSuccess(chatResultVo)
                    }
                }
            }
        })
    }
}

@Throws(RuntimeException::class)
fun error(msg: String) {
    throw RuntimeException(msg)
}